// PathRequestDispatcher.java - A path-based RequestDispatcher.
//
// Copyright (C) 2001-2002  Smart Software Consulting
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//
// Smart Software Consulting
// 1688 Silverwood Court
// Danville, CA  94526-3079
// USA
//
// http://www.smartsc.com
//

package com.smartsc.http;

import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;

import javax.servlet.RequestDispatcher;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import javax.servlet.http.HttpUtils;

import com.smartsc.http.HttpRequest;
import com.smartsc.http.HttpResponse;
import com.smartsc.http.HttpServer;

public class
PathRequestDispatcher
implements RequestDispatcher
{
	protected
	PathRequestDispatcher( NamedRequestDispatcher namedDispatcher,
		String requestURI, String servletPath,
		String pathInfo, String queryString )
	{
		this.namedDispatcher = namedDispatcher;
		this.requestPath = requestURI;
		this.requestURI = requestURI;
		this.servletPath = servletPath;
		this.pathInfo = pathInfo;
		if( queryString != null )
		{
			requestPath += "?" + queryString;
			this.queryString = queryString;
			queryParameters = HttpUtils.parseQueryString( queryString );
		}
	}

	public void forward( ServletRequest req, ServletResponse res)
	throws ServletException, IOException
	{
		HttpRequest httpReq = (HttpRequest)req;

		// Save current path elements of request
		String oldRequestPath = httpReq.requestPath;
		String oldRequestURI = httpReq.requestURI;
		String oldServletPath = httpReq.servletPathAndName[0];
		String oldServletName = httpReq.servletPathAndName[1];
		String oldPathInfo = httpReq.pathInfo;
		String oldQueryString = httpReq.queryString;

		// Set new path elements of request
		httpReq.requestPath = requestPath;
		httpReq.requestURI = requestURI;
		httpReq.servletPathAndName[0] = servletPath;
		httpReq.servletPathAndName[1] = namedDispatcher.getName();
		httpReq.pathInfo = pathInfo;
		httpReq.queryString = queryString;

		// Push query string parameters into request
		pushQueryParameters( httpReq );

		try
		{
			namedDispatcher.forward( req, res );
		}
		finally
		{
			// The response is now closed, but the request should still
			// be restored to its previous state in case the forwarding
			// servlet expects things to be unchanged.

			// Restore original path elements of request
			httpReq.requestPath = oldRequestPath;
			httpReq.requestURI = oldRequestURI;
			httpReq.servletPathAndName[0] = oldServletPath;
			httpReq.servletPathAndName[1] = oldServletName;
			httpReq.pathInfo = oldPathInfo;
			httpReq.queryString = oldQueryString;

			// Pop query string parameters from request
			popQueryParameters( httpReq );
		}
	}

	public void include( ServletRequest req, ServletResponse res)
	throws ServletException, IOException
	{
		HttpRequest httpReq = (HttpRequest)req;

		// Save current included path info
		String oldRequestURI = (String)httpReq.getAttribute( INCLUDE_REQUEST_URI );
		String oldContextPath = (String)httpReq.getAttribute( INCLUDE_CONTEXT_PATH );
		String oldServletPath = (String)httpReq.getAttribute( INCLUDE_SERVLET_PATH );
		String oldPathInfo = (String)httpReq.getAttribute( INCLUDE_PATH_INFO );
		String oldQueryString = (String)httpReq.getAttribute( INCLUDE_QUERY_STRING );

		// Set new included path info
		httpReq.setAttribute( INCLUDE_REQUEST_URI, requestURI );
		httpReq.setAttribute( INCLUDE_CONTEXT_PATH, "/" );
		httpReq.setAttribute( INCLUDE_SERVLET_PATH, servletPath );
		httpReq.setAttribute( INCLUDE_PATH_INFO, pathInfo );
		httpReq.setAttribute( INCLUDE_QUERY_STRING, queryString );

		// Push query string parameters into request
		pushQueryParameters( httpReq );

		try
		{
			namedDispatcher.forward( req, res );
		}
		finally
		{
			// Restore original included path info
			httpReq.setAttribute( INCLUDE_REQUEST_URI, oldRequestURI );
			httpReq.setAttribute( INCLUDE_CONTEXT_PATH, oldContextPath );
			httpReq.setAttribute( INCLUDE_SERVLET_PATH, oldServletPath );
			httpReq.setAttribute( INCLUDE_PATH_INFO, oldPathInfo );
			httpReq.setAttribute( INCLUDE_QUERY_STRING, oldQueryString );

			// Pop query string parameters from request
			popQueryParameters( httpReq );
		}
	}

	protected void pushQueryParameters( HttpRequest httpReq )
	{
		// Set query string parameters in request
		if( queryParameters != null )
		{
			Enumeration keys = queryParameters.keys();
			while( keys.hasMoreElements() )
			{
				String key = (String)keys.nextElement();
				String value = (String)queryParameters.get( key );
				httpReq.pushParameter( key, value );
			}
		}
	}

	protected void popQueryParameters( HttpRequest httpReq )
	{
		// Pop query string parameters from request
		if( queryParameters != null )
		{
			Enumeration keys = queryParameters.keys();
			while( keys.hasMoreElements() )
			{
				String key = (String)keys.nextElement();
				httpReq.popParameter( key );
			}
		}
	}

	protected NamedRequestDispatcher namedDispatcher;
	protected String requestPath;
	protected String requestURI;
	protected String servletPath;
	protected String pathInfo;
	protected String queryString;
	protected Hashtable queryParameters;

	public static final String INCLUDE_REQUEST_URI =
		"javax.servlet.include.request_uri";

	public static final String INCLUDE_CONTEXT_PATH = "";

	public static final String INCLUDE_SERVLET_PATH =
		"javax.servlet.include.servlet_path";

	public static final String INCLUDE_PATH_INFO =
		"javax.servlet.include.path_info";

	public static final String INCLUDE_QUERY_STRING =
		"javax.servlet.include.query_string";
}
